#!/usr/bin/env python
# -* coding: utf-8 -*-

#Author: Gustaw Lewandowski
#Licence : GPL

import sys
import os
import time
import shutil
import urwid
import encodings
import logging
from mutex import mutex

import ucp

utf8decode = lambda s: encodings.codecs.utf_8_decode(s)[0]
log = logging.getLogger('urwidcmd')
# long line vertical line for separating two columns
divider = ('fixed', 1, urwid.AttrWrap(urwid.SolidFill(utf8decode("│")), "dir"))

dict = None
try:
	import enchant
except ImportError:
	log.info("No enchant module. No spellcheck :(")
else:
	import enchant
	dict = enchant.Dict("pl_PL")

pygments_enable = False
try:
	import pygments
except ImportError:
	log.info("No pygments module. No source highlight :(")
else:
	#pygments_enable = True
	pygments_enable = False

def trunc(x):
	r = round(x)
	if (x - r) > 0.0:
		return int(r) + 1
	return int(r)

class AreYouSureForMulti():
	def __init__(self, view, text, exe, final_mutex):
		self.m = mutex()
		self.exe = exe
		self.exitcode = None
		self.text = text
		self.view = view
		self.final_mutex = final_mutex
		self.do_final = True		
		self.body = view.get_body()
		self.window = AreYouSureDialogMultiWindow(view.get_body(), log, text, self.back)
	def question(self, file_name):
		self.m.lock(self.run, file_name)
	def run(self, file_name):
		if self.exitcode in (2,3):
			self.m.unlock()
			if self.exitcode == 2:
				if self.exe(file_name) == False:
					self.do_final = False
		else:
			self.current = file_name
			self.window.w.set_text("Delete \'" + file_name + "\'?")
			self.view.set_body(self.window)
	def back(self, button):
		self.view.set_body(self.body)
		self.exitcode = button.exitcode
		self.do_exe()
	
	def do_exe(self):
		if self.exitcode in (0,2):
			log.info(self.current + " kasuj")
			if self.exe(self.current) == False:
				self.do_final = False
		else:
			self.do_final = False
		self.m.unlock()
		if self.m.test() == 0 and self.do_final == True:
			self.final_mutex.unlock()		

class Border(urwid.WidgetWrap):
	def __init__(self, widget, title=None, attr=None):
		"""
		Draw a line border around widget and set up a title
		@param widget: Widget to wrap
		@param title: (title, attribute) tuple or basestring
		@type title: urwid.Text object
		@param attr: Attribute of border
		@single_top: single or double line on top
		"""

		_title = None
		_len = 0
		
		double_top = setup.doubleTitleBar
		if title is not None:
			if isinstance(title, tuple):
				_len = len(title[0])
				_title = urwid.Text((title[1], " %s " % title[0]), "center")
			elif isinstance(title, basestring):
				_len = len(title)
				_title = urwid.Text(title, "center")
			else:
				raise UIError("Invalid title type %s. "\
					"Tuple or basetring expected" % type(_title))
		_len += 2
		self.attr = attr

		self.bline = self._attr(urwid.Divider(utf8decode("─")))
		self.vline = self._attr(urwid.SolidFill(utf8decode("│")))

		self.blcorner = self._attr(urwid.Text(utf8decode("└")))
		self.brcorner = self._attr(urwid.Text(utf8decode("┘")))

		if double_top == False:
			self.tline = self._attr(urwid.Divider(utf8decode("─")))
			self.tlcorner = self._attr(urwid.Text(utf8decode("┌")))
			self.trcorner = self._attr(urwid.Text(utf8decode("┐")))
		else:
			self.tline = self._attr(urwid.Divider(utf8decode("═")))
			self.tlcorner = self._attr(urwid.Text(utf8decode("╒")))
			self.trcorner = self._attr(urwid.Text(utf8decode("╕")))

		tline_widgets = [('fixed', 1, self.tlcorner), self.tline]

		if title is not None:
			tline_widgets.append(("fixed", _len, _title))

		tline_widgets.extend([self.tline, ("fixed", 1, self.trcorner)])

		self.top = urwid.Columns(tline_widgets)
		self.middle = urwid.Columns([('fixed', 1, self.vline),
						widget, ('fixed', 1, self.vline)],
						box_columns=[0,2], focus_column=1)

		self.bottom = urwid.Columns([('fixed', 1, self.blcorner),
						self.bline, ('fixed', 1, self.brcorner)])

		self.pile = urwid.Pile([('flow',self.top), self.middle,
					('flow', self.bottom)], focus_item=1)

		super(Border, self).__init__(self.pile)

	def _attr(self, widget):
		if self.attr is None:
			return widget
		return urwid.AttrWrap(widget, self.attr)
	def get_widget(self):
		"""Return wrapped widget"""
		return self.middle.widget_list[1]


class DialogWindow(urwid.Overlay):
	def __init__(self, w, bottom_w, buttons, back=None, title=None, bg='dialog'):
		self.back = back
		self.footer = self.add_buttons(buttons, back)
		self.w = w
		self.body = urwid.Pile([urwid.Filler(self.w),\
					urwid.Filler(urwid.Divider(" ")),\
					urwid.Filler(self.footer)],\
					focus_item = 2)
		w = urwid.AttrWrap(Border(self.body, title), bg)
		# shadow effect
		w = urwid.Columns([w, ('fixed', 2, urwid.AttrWrap(
			urwid.Filler(urwid.Text(('border', '  ')), 'top'), 'shadow'))])
		w = urwid.Frame(w ,footer = urwid.AttrWrap(urwid.Text(('border', '  ')), 'shadow'))
		self.drag_mode = False
		return self.__super.__init__(w, bottom_w, 'center', ('relative', 70) , 'middle', 8)
	
	
	def keypress(self, size, key):
		if key == "esc":
			return (ucp.urwidcmd, ucp.UrwidCmdProc.restore, self.bottom_w)
		if key == "down":
			left, right, top, bottom = self.calculate_padding_filler(size, False)
			self.valign_amount = top + 1 
			self.valign_type = 'fixed top'
			self._invalidate()
		elif key == "up":
			left, right, top, bottom = self.calculate_padding_filler(size, False)
			if top is not 0:
				self.valign_amount = top - 1 
			self.valign_type = 'fixed top'
			self._invalidate()
		elif key == "left":
			if self.buttons.focus_cell.last is 'left':
				left, right, top, bottom = self.calculate_padding_filler(size, False)
				if left is not 0:
					self.align_amount = left - 1 
				self.align_type = 'fixed left'
				self._invalidate()
		elif key == "right":
			if self.buttons.focus_cell.last is 'right':
				left, right, top, bottom = self.calculate_padding_filler(size, False)
				self.align_amount = left + 1 
				self.align_type = 'fixed left'
				self._invalidate()
		return self.__super.keypress(size, key)


	def mouse_event(self, size, event, button, col, row, focus):
		""" Handle drag of dialog box (I hope)"""
		if event == 'mouse drag':
			left, right, top, bottom = self.calculate_padding_filler(size, False)
			w, h = size
			col += 1
			if self.drag_mode == True:
				col_delta = col - self.old_col
				row_delta = row - self.old_row
				if not (col_delta == 0 or (left + col_delta) < 0):
					self.align_type = 'fixed left'
					self.align_amount = left + col_delta
					self.old_col = col
					self._invalidate()					
				if not (row_delta == 0 or (top + row_delta) < 0):
					self.valign_type = 'fixed top'
					self.valign_amount = top + row_delta
					self.old_row = row
					self._invalidate()					
			else:
				top -= 1
				if row > top and row < h - bottom:
					if col > left and col < w - right:
						# hit in window
						self.drag_mode = True
						self.old_col = col
						self.old_row = row
						log.info('drag_mode')
		elif event == 'mouse release':
			self.drag_mode = False
		else:
			self.__super.mouse_event(size, event, button, col, row, focus)

	def add_buttons(self, buttons, back):
		l = []
		max_label = max(map(len, map(lambda x: x[0], buttons)))
		for name, exitcode in buttons:
			b = ButtonSqr(name.center(max_label), back)
			b.exitcode = exitcode
			b.last = None
			b = urwid.AttrWrap(b, 'dialog', 'FxKey')
			l.append(b)
		# it's needed for mark first/last buttons in case window move
		b.last = 'right'
		l[0].last = 'left'

		self.buttons = urwid.GridFlow(l, max_label + 4, 2, 1, 'center')
		return self.buttons		

class InputDialog(DialogWindow):
	def __init__(self, bottom_w, text, back=None):
		w = urwid.AttrWrap(urwid.Edit("", text), 'edit')
		#w = urwid.Columns([('fixed', 2, urwid.Divider(" ")),\
		#		w,\
		#		('fixed', 2, urwid.Divider(" "))],\
		#		box_columns=[0,2], focus_column=1)
		self.__super.__init__(w, bottom_w,\
					[("OK", 0), ("Cancel", 1)],\
					 back, "Question")


class AreYouSureDialog(DialogWindow):
	def __init__(self, bottom_w, text, back=None):
		self.__super.__init__(urwid.Text(text), bottom_w,\
					[("OK", 0), ("Cancel", 1)],\
					 back, "Question")

class AreYouSureDialogMultiWindow(DialogWindow):
	def __init__(self, bottom_w, text, back=None):
		self.__super.__init__(urwid.Text(text), bottom_w,\
					[("OK", 0), ("Cancel", 1), ("All", 2), ("Never", 3)],\
					 back, "Question")

class FailureWindow(DialogWindow):
	def __init__(self, bottom_w, text, back=None):
		self.__super.__init__(urwid.Text(text), bottom_w,\
					[("OK", 0)],\
					 back, "Error", bg='error')

class ButtonSqr(urwid.Button):
	button_left = urwid.Text("[")
	button_right = urwid.Text("]")
	def __init__(self, label, on_press=None, user_data=None):
		self.__super.__init__(label, on_press, user_data)

class ListWidget(urwid.WidgetWrap):
	def keypress(self, (maxcol,), key):
		return key

#	def mouse_event(self, size, event, button, col, row, focus):
#		log.info('mouse event')

	def selectable(self):
		return True

class ListWalkerBase(urwid.ListWalker):
	""" ListWalker base """
	lines = []
	focus, oldfocus = (0, 0)
	def __init__(self, size=0):
		""" Test constructor - creates list of numbers """
		self.size = size
		if size != 0:
			for i in range(0, self.size):
				self.lines.append(ListWidget(str(i)))

	def _get_at_pos(self, pos):
		if pos >= 0 and pos < self.size:
			return self.lines[pos], pos
		else:
			return None, None

	def get_focus(self):
		return self._get_at_pos(self.focus)

	def get_focus_entry(self):
		return self.lines[self.focus]

	def set_focus(self, focus):
		if self.focus != focus:
			self.focus = focus
			self._modified()

	def get_next(self, pos):
		return self._get_at_pos(pos + 1)

	def get_prev(self, pos):
		return self._get_at_pos(pos - 1)

	def go_first(self):
		self.set_focus(0)

	def go_last(self):
		self.set_focus(self.size - 1)

	def set_lines(self, lines):
		""" set lines, size, and call refresh"""
		self.lines = lines[:]
		self.size = len(lines)
		self.focus, self.oldfocus = self.oldfocus, self.focus
		if self.size < (self.focus+1):
			self.focus = self.size-1
		self._modified()		

class ListBoxBase(urwid.ListBox):
	""" ListBox with 'home' and 'end' keypress and other events"""
	def __init__(self, walker):
		return self.__super.__init__(walker)

	def mouse_event(self, size, event, button, col, row, focus):
		""" Handle mouse up/down rolling """
		log.info(button)
		if button == 5:
			return self.__super.keypress((col, row), 'down')
		elif button == 4:
			return self.__super.keypress((col, row), 'up')
		else:
			return self.__super.mouse_event(size, event, button, col, row, focus)
	
	def keypress(self,(maxcol, maxrow), key):
		""" Handle home and end keys """
		if key == "home":
			self.body.go_first()	
		elif key == "end":
			self.body.go_last()
		rc = self.__super.keypress((maxcol, maxrow), key)
		return rc

class ListBoxSB(urwid.Columns):
	"""  ListBoxBase with scrollbar """
	def __init__(self, listBox):
		self.old_rows = self.old_offset = self.old_lsize = -1
		self.visible = True
		self.listBox = listBox
		self.body = listBox.body
		self.text = urwid.Text(u"")
		self.scrollBar = urwid.AttrWrap(urwid.Filler(urwid.Padding(self.text, 'left', 1)) , 'linenr')
		self.__super.__init__([self.listBox, ('fixed', 1, self.scrollBar)], focus_column=0)

	def hide_scroll_bar(self):
		if self.visible == True:
			del self.widget_list[1]
			del self.column_types[1]
			self.visible = False
			self.old_rows = self.old_offset = self.old_lsize = -1

	def show_scroll_bar(self):
		if self.visible == False:
			self.column_types.append(('fixed', 1))
			self.widget_list.append(self.scrollBar)
			self.visible = True

	def render(self, size, focus=False):
		junk, rows = size
		lsize = self.listBox.body.size
		offset = self.listBox.body.focus - self.listBox.offset_rows
		if rows < lsize:
			if (rows, lsize) != (self.old_rows, self.old_lsize):
				self.d1 = float(lsize)/rows
				self.d2 = round((rows-2)/self.d1) # how many chars is needed for one step bar
				if rows-2-self.d2 > 0:
					self.d3 = float(lsize-rows)/(rows-2-self.d2)
				else:
					self.d3 = float(lsize-rows)
				self.d4 = int(offset/self.d3) #which step
				(self.old_rows, self.old_lsize) = (rows, lsize)
			elif offset != self.old_offset:
				d4 = int(offset/self.d3)
				if self.d4 == d4:
					return self.__super.render(size, focus)
				(self.d4, self.old_offset) = (d4, offset)
			else:
				#scrollbar needed but not changed
				rt = self.__super.render(size, focus)
				self.set_focus_column(0)
				return rt
				
			above = u'█'*self.d4
			below = u'█'*(rows-self.d4-2-trunc(self.d2))
			self.text.set_text([('edit', u"▲"), above + u"\u2592"*trunc(self.d2) + below, ('edit', u"▼")])
			(self.old_rows, self.old_offset, self.old_lsize) = (rows, offset, lsize)
			self.show_scroll_bar()
		else:
			# we don't need scroll bar
			self.hide_scroll_bar()
		rt = self.__super.render(size, focus)
		self.set_focus_column(0)
		return rt

class Setup():
	doubleTitleBar = True
	logFile = "/tmp/urwidcmd.log"
	frameDirView = False
	#listEntryAttr = urwid.AttrSpec('#123', 'g30')
	dirWidget = None
	
	def apply(self):
		""" Apply changes """
		if self.dirWidget != None: 
			self.dirWidget.with_frame(self.frameDirView)
	
setup = Setup()
	
def utilInit():
	hdlr = logging.FileHandler('/tmp/urwidcmd.log')
	formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
	hdlr.setFormatter(formatter)
	log.addHandler(hdlr)
	log.setLevel(logging.DEBUG)
	log.info("="*15 + " start " + "="*15)

